from __future__ import annotations
import os
import threading
import queue
from pathlib import Path
import tkinter as tk
from tkinter import messagebox
from PIL import Image, ExifTags

# HEIC 支持检测（与 app_gui 中的逻辑保持一致，避免循环导入）
try:
    from pillow_heif import register_heif_opener

    register_heif_opener()
    HEIC_SUPPORTED = True
except ImportError:
    HEIC_SUPPORTED = False

from calc_utils import convert_to_pixels, calculate_optimal_dpi_for_image as _calc_optimal_dpi_for_image


class ProcessingMixin:
    """负责耗时图片处理、线程与进度条更新的混入类"""

    # ---- 工具函数包装 ---------------------------------------------------
    def convert_to_pixels(self, value, unit, dpi):
        """包装顶层 convert_to_pixels，保持旧的 self.convert_to_pixels 调用不变"""
        return convert_to_pixels(value, unit, dpi)

    def calculate_optimal_dpi_for_image(self, original_width, original_height, target_width_inch, target_height_inch):
        """包装顶层 calculate_optimal_dpi_for_image，保持旧接口"""
        return _calc_optimal_dpi_for_image(original_width, original_height, target_width_inch, target_height_inch)

    def start_process(self):
        """开始处理图片"""
        # 检查是否已选择源和目标
        if not self.selected_files:
            messagebox.showwarning("警告", "请先选择要处理的图片！")
            return

        if not os.path.exists(self.dest_path.get()):
            messagebox.showwarning("警告", "请选择有效的保存路径！")
            return

        try:
            # 验证JPG质量设置
            quality = 95  # 默认值
            if self.format_var.get() == "jpg":
                quality = int(self.quality_var.get())
                if not (1 <= quality <= 100):
                    raise ValueError("JPG质量必须在1-100之间")

            # 检查图片尺寸和DPI的统一性
            uniform_format, uniform_width, uniform_height, uniform_dpi, uniform_size = self.check_images_uniformity()

            # 判断是否是尺寸相同但DPI不同的情况
            is_same_size_diff_dpi = uniform_size and not uniform_dpi and len(self.selected_files) > 1

            # 获取用户设置的DPI值
            dpi_str = self.dpi_var.get().strip()
            user_dpi = float(dpi_str) if self.dpi_enabled.get() and dpi_str else None
            current_unit = self.unit_var.get()

            # 获取需要处理的文件列表
            if self.mode_var.get() == "folder":
                source = self.source_path.get()
                if not source:
                    messagebox.showerror("错误", "请选择源文件夹")
                    return

                image_files = []
                supported_exts = ['*.jpg', '*.jpeg', '*.png', '*.bmp']
                if HEIC_SUPPORTED:
                    supported_exts.extend(['*.heic', '*.heif'])
                # 使用递归搜索（**表示递归搜索所有子文件夹）
                for ext in supported_exts:
                    image_files.extend(Path(source).rglob(ext))

                # 转换为与all_images_info格式相兼容的形式，并保存相对路径
                self.all_images_info = []
                source_path_obj = Path(source)
                for path in image_files:
                    try:
                        relative_path = path.relative_to(source_path_obj)
                    except ValueError:
                        relative_path = None
                    self.all_images_info.append({
                        'path': str(path),
                        'relative_path': relative_path
                    })
            else:
                if not self.selected_files:
                    messagebox.showerror("错误", "请选择要处理的图片文件")
                    return
                image_files = [Path(f) for f in self.selected_files]

            if not self.all_images_info:
                messagebox.showwarning("警告", "没有找到需要处理的图片文件")
                return

            # 重置进度条
            self.progress['value'] = 0
            self.progress['maximum'] = len(self.all_images_info)

            # 处理每一张图片
            for img_info in self.all_images_info:
                img_path = Path(img_info['path'])
                try:
                    # 尝试打开图像文件，对 RAW 文件做特殊错误处理
                    try:
                        img = self.open_image_file(img_path)
                    except Exception as e:
                        if self.is_raw_file(img_path):
                            error_msg = f"RAW文件处理失败: {str(e)}"
                            self.skip_problematic_file(img_path, error_msg)
                            continue  # 跳过此文件继续处理
                        else:
                            raise e

                    with img:
                        # 获取原始尺寸（考虑方向）
                        original_width, original_height = self.get_oriented_size(img)

                        # 保持原始DPI，除非用户明确要求修改
                        original_dpi = img.info.get('dpi', (72, 72))[0]
                        # 修复：当DPI输入框勾选但为空时，使用原图DPI
                        output_dpi = user_dpi if user_dpi is not None else original_dpi

                        # 首先处理EXIF方向，确保图片方向正确
                        try:
                            # 获取EXIF方向
                            orientation = None
                            if hasattr(img, '_getexif') and img._getexif():
                                exif = img._getexif()
                                for tag, tag_value in ExifTags.TAGS.items():
                                    if tag_value == 'Orientation':
                                        if tag in exif:
                                            orientation = exif[tag]
                                            break

                            # 根据EXIF方向旋转图片
                            if orientation == 2:
                                img = img.transpose(Image.FLIP_LEFT_RIGHT)
                            elif orientation == 3:
                                img = img.transpose(Image.ROTATE_180)
                            elif orientation == 4:
                                img = img.transpose(Image.FLIP_TOP_BOTTOM)
                            elif orientation == 5:
                                img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_90)
                            elif orientation == 6:
                                img = img.transpose(Image.ROTATE_270)
                            elif orientation == 7:
                                img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_270)
                            elif orientation == 8:
                                img = img.transpose(Image.ROTATE_90)
                        except Exception:
                            # 如果无法读取EXIF或出现其他错误，保持原样
                            pass

                        # 获取旋转后的尺寸
                        oriented_width, oriented_height = img.size

                        # 处理尺寸修改
                        if self.size_enabled.get():
                            # 使用旋转后的尺寸作为原始尺寸
                            original_width, original_height = oriented_width, oriented_height
                            modified_width = self.width_var.get().strip()
                            modified_height = self.height_var.get().strip()

                            # 确定最终的宽度和高度
                            final_width = original_width  # 默认使用原图宽度
                            final_height = original_height  # 默认使用原图高度

                            # 批量保持比例模式的处理
                            if self.size_enabled.get() and self.batch_maintain_ratio.get() and len(
                                    self.selected_files) > 1 and not uniform_size:
                                # 调用批量保持比例处理方法
                                final_width, final_height, new_dpi = self.process_batch_ratio(original_width,
                                                                                              original_height,
                                                                                              original_dpi, uniform_dpi)
                                # 如果返回了新的DPI值，则使用它
                                if new_dpi is not None:
                                    output_dpi = new_dpi
                            # 特殊情况处理：尺寸相同但DPI不同
                            elif is_same_size_diff_dpi and self.maintain_ratio.get() and self.dpi_enabled.get():
                                if current_unit == "pixel":
                                    if user_dpi is not None:
                                        # 有DPI值时，使用DPI计算新尺寸
                                        img_dpi = float(original_dpi)
                                        # 计算物理尺寸（英寸）
                                        width_inch = original_width / img_dpi
                                        height_inch = original_height / img_dpi
                                        # 使用新DPI计算像素尺寸
                                        final_width = int(width_inch * user_dpi)
                                        final_height = int(height_inch * user_dpi)
                                    else:
                                        # DPI为空时，检查用户是否输入了宽高值
                                        if modified_width and modified_height:
                                            # 使用用户输入的宽高值
                                            final_width = self.convert_to_pixels(
                                                modified_width,
                                                current_unit,
                                                original_dpi
                                            )
                                            final_height = self.convert_to_pixels(
                                                modified_height,
                                                current_unit,
                                                original_dpi
                                            )
                                        else:
                                            # 使用原始尺寸
                                            final_width = original_width
                                            final_height = original_height
                                else:  # 厘米或英寸单位
                                    if user_dpi is not None:
                                        # 有DPI值时，检查用户是否输入了宽高值
                                        if modified_width and modified_height:
                                            # 使用用户输入的宽高值
                                            final_width = self.convert_to_pixels(
                                                modified_width,
                                                current_unit,
                                                user_dpi
                                            )
                                            final_height = self.convert_to_pixels(
                                                modified_height,
                                                current_unit,
                                                user_dpi
                                            )
                                        else:
                                            # 使用原始尺寸，根据新DPI计算
                                            img_dpi = float(original_dpi)
                                            width_inch = original_width / img_dpi
                                            height_inch = original_height / img_dpi
                                            final_width = int(width_inch * user_dpi)
                                            final_height = int(height_inch * user_dpi)
                                    else:
                                        # DPI为空时使用原始尺寸
                                        final_width = original_width
                                        final_height = original_height
                            else:
                                # 常规情况：正常处理用户输入的宽高
                                if modified_width:
                                    final_width = self.convert_to_pixels(
                                        modified_width,
                                        self.unit_var.get(),
                                        output_dpi
                                    )
                                if modified_height:
                                    final_height = self.convert_to_pixels(
                                        modified_height,
                                        self.unit_var.get(),
                                        output_dpi
                                    )

                            # 确保宽度和高度是整数
                            final_width = int(round(final_width))
                            final_height = int(round(final_height))

                            # 只在尺寸确实发生变化时才进行尺寸调整
                            if final_width != original_width or final_height != original_height:
                                try:
                                    # 改进图像缩放算法，实现更接近Photoshop的效果
                                    # 判断图像类型，选择最适合的重采样方法
                                    is_enlarging = final_width > original_width or final_height > original_height
                                    is_line_art = img.mode in ['1', 'L', 'P'] or (
                                            img.mode == 'RGBA' and len(img.getcolors(256)) < 256)

                                    # 对于放大，使用不同的算法
                                    if is_enlarging:
                                        # 放大时，Photoshop通常使用更平滑的算法
                                        if is_line_art:
                                            # 线条艺术、图标等使用BICUBIC更锐利
                                            img = img.resize((final_width, final_height), Image.BICUBIC)
                                        else:
                                            # 照片类图像使用LANCZOS
                                            img = img.resize((final_width, final_height), Image.LANCZOS)
                                    else:
                                        # 缩小时，根据图像类型选择不同算法
                                        if is_line_art:
                                            # 对于线条艺术，使用BICUBIC能保持锐利边缘
                                            img = img.resize((final_width, final_height), Image.BICUBIC)
                                        else:
                                            # 照片使用LANCZOS算法
                                            img = img.resize((final_width, final_height), Image.LANCZOS)

                                    # 对PNG图像进行锐化处理，模拟Photoshop的"锐化已缩放图像"行为
                                    output_ext = self.get_output_extension(img_path)
                                    if output_ext.lower() == '.png' and not is_enlarging and img.mode not in ['P', '1']:
                                        # 轻微锐化以恢复细节，类似Photoshop"智能锐化"
                                        from PIL import ImageFilter
                                        img = img.filter(ImageFilter.UnsharpMask(radius=0.5, percent=80, threshold=3))
                                except Exception as e:
                                    print(f"调整尺寸时出错: {e}")
                                    # 如果出现错误，尝试使用其他调整方法
                                    img = img.resize((final_width, final_height), Image.BICUBIC)
                        else:
                            # 如果未启用尺寸修改，保持原始尺寸
                            final_width = oriented_width
                            final_height = oriented_height

                        # 确定输出格式和路径
                        output_ext = self.get_output_extension(img_path)

                        # 查找对应的图片信息以获取相对路径
                        relative_path = None
                        for img_info in self.all_images_info:
                            if img_info['path'] == str(img_path):
                                relative_path = img_info.get('relative_path')
                                break

                        # 根据是否有相对路径来构建输出路径
                        if relative_path:
                            # 保持原有文件夹结构
                            output_dir = Path(self.dest_path.get()) / relative_path.parent
                            output_dir.mkdir(parents=True, exist_ok=True)  # 创建必要的文件夹
                            output_path = output_dir / (img_path.stem + output_ext)
                        else:
                            # 直接输出到目标文件夹（兼容原有逻辑）
                            output_path = Path(self.dest_path.get()) / (img_path.stem + output_ext)

                        # 处理图片格式转换
                        if output_ext.lower() in ['.jpg', '.jpeg']:
                            # 对于JPEG格式，处理各种输入模式
                            if img.mode in ['RGBA', 'LA']:
                                # 创建白色背景
                                background = Image.new('RGB', img.size, (255, 255, 255))
                                if img.mode == 'RGBA':
                                    background.paste(img, mask=img.split()[3])  # 使用alpha通道作为蒙版
                                else:
                                    background.paste(img, mask=img.split()[1])  # 使用alpha通道作为蒙版
                                img = background
                            elif img.mode not in ['RGB']:
                                img = img.convert('RGB')
                        elif output_ext.lower() == '.png':
                            # 改进的PNG处理，更接近Photoshop效果
                            if img.mode == 'RGBA':
                                pass  # 保持RGBA模式不变
                            elif img.mode == 'LA':
                                pass  # 保持LA模式不变
                            elif img.mode == 'P' and 'transparency' in img.info:
                                # 对于有透明度的索引颜色模式，保留原样或转换为RGBA以保持精确的透明度
                                pass  # 保持原模式
                            elif img.mode == 'P':
                                pass  # 保持索引颜色模式不变，保留精确的颜色信息
                            elif img.mode == 'L':
                                pass  # 保持灰度模式不变
                            elif img.mode == '1':
                                pass  # 保持二值模式不变
                            else:
                                # 其他模式才考虑转为RGB
                                img = img.convert('RGB')

                        # 准备保存参数
                        save_args = {
                            'dpi': (output_dpi, output_dpi)  # 设置输出DPI
                        }

                        # 添加格式特定的保存参数
                        if output_ext.lower() in ['.jpg', '.jpeg']:
                            save_args['quality'] = int(self.quality_var.get())
                            save_args['optimize'] = True  # 启用优化
                        elif output_ext.lower() == '.png':
                            # 添加PNG特定的高质量保存选项，模拟Photoshop行为
                            save_args['optimize'] = True  # 启用文件大小优化
                            save_args['compress_level'] = 9  # 最高压缩级别(0-9)
                            # 保留索引颜色模式的调色板
                            if img.mode == 'P' and 'palette' in img.info:
                                save_args['palette'] = img.info['palette']
                            # 保留透明度信息
                            if 'transparency' in img.info:
                                save_args['transparency'] = img.info['transparency']
                        elif output_ext.lower() in ['.heic', '.heif']:
                            # HEIC格式保存参数
                            if HEIC_SUPPORTED:
                                save_args['quality'] = int(self.quality_var.get()) if hasattr(self,
                                                                                              'quality_var') else 95
                                # HEIC格式通常需要RGB模式
                                if img.mode not in ['RGB', 'RGBA']:
                                    img = img.convert('RGB')
                            else:
                                # 如果不支持HEIC，转换为JPG格式保存
                                output_path = output_path.with_suffix('.jpg')
                                save_args['quality'] = int(self.quality_var.get()) if hasattr(self,
                                                                                              'quality_var') else 95
                                save_args['optimize'] = True
                                if img.mode not in ['RGB']:
                                    img = img.convert('RGB')

                        # 保存处理后的图片
                        img.save(output_path, **save_args)

                        # 更新进度条
                        self.progress['value'] += 1
                        self.root.update_idletasks()

                except Exception as e:
                    messagebox.showerror("错误", f"处理图片 {img_path.name} 时发生错误:\n{str(e)}")
                    continue

            messagebox.showinfo("完成", "图片处理完成！")

        except Exception as e:
            messagebox.showerror("错误", f"处理过程中发生错误:\n{str(e)}")

    def start_threaded_process(self):
        """启动后台线程处理图片"""
        # 检查是否已选择源和目标
        if not self.selected_files:
            messagebox.showwarning("警告", "请先选择要处理的图片！")
            return

        dest_folder = self.dest_path.get()
        if not os.path.exists(dest_folder):
            messagebox.showwarning("警告", "请选择有效的保存路径！")
            return

        try:
            # --- 从滑块获取JPG质量 (0-12) ---
            self.processing_user_quality_input = 10  # 设置一个默认值
            if self.format_var.get() == "jpg":
                # 仅在格式为JPG时，尝试从滑块变量读取
                if hasattr(self, 'quality_slider_var'):
                    self.processing_user_quality_input = self.quality_slider_var.get()
                else:
                    # 如果滑块变量意外丢失，记录错误并阻止处理
                    print("错误: JPG质量滑块变量(quality_slider_var)未找到.")
                    messagebox.showerror("内部错误", "无法获取JPG质量设置。请重启程序。")
                    # 恢复按钮状态
                    self.start_btn.config(state=tk.NORMAL)
                    if hasattr(self, 'cancel_btn'): self.cancel_btn.config(state=tk.DISABLED)
                    return
            # 如果格式不是JPG, self.processing_user_quality_input 会保持默认值10，
            # 但它在 _process_single_image 中只在处理JPG时使用。
            # --- 结束获取JPG质量 ---

            # --- 添加状态获取 ---
            # 获取图片统一性信息
            _, _, _, self.processing_uniform_dpi, self.processing_uniform_size = self.check_images_uniformity()
            self.processing_is_same_size_diff_dpi = self.processing_uniform_size and not self.processing_uniform_dpi and len(
                self.selected_files) > 1

            # 获取用户设置的DPI和单位
            dpi_str = self.dpi_var.get().strip()
            self.processing_dpi_enabled = self.dpi_enabled.get()
            self.processing_user_dpi = float(dpi_str) if self.processing_dpi_enabled and dpi_str else None
            self.processing_current_unit = self.unit_var.get()

            # 获取尺寸和比例设置
            self.processing_size_enabled = self.size_enabled.get()
            self.processing_maintain_ratio = self.maintain_ratio.get()
            self.processing_batch_maintain_ratio = self.batch_maintain_ratio.get()
            self.processing_resampling_enabled = self.resampling_enabled.get()  # 获取重采样状态

            # 获取用户输入的尺寸（如果尺寸修改启用）
            self.processing_modified_width = self.width_var.get().strip() if self.processing_size_enabled else ""
            self.processing_modified_height = self.height_var.get().strip() if self.processing_size_enabled else ""

            # 获取输出格式设置
            self.processing_format = self.format_var.get()

            # 获取标准尺寸模式状态
            self.processing_standard_size_applied = getattr(self, 'standard_size_applied', False)
            self.processing_standard_size_selection = self.standard_size_var.get() if hasattr(self,
                                                                                              'standard_size_var') else "自定义"
            # --- 结束状态获取 ---

            if not self.all_images_info:
                messagebox.showwarning("警告", "没有找到需要处理的图片文件")
                return

            # 禁用开始按钮，启用取消按钮
            self.start_btn.config(state=tk.DISABLED)
            self.cancel_btn.config(state=tk.NORMAL)  # Ensure cancel_btn exists and is configured

            # --- 重置自定义进度条 ---
            # self.progress['value'] = 0
            # self.progress['maximum'] = len(self.all_images_info)
            # self.progress_label.config(text="0% (0/{})".format(len(self.all_images_info)))
            self.update_progress_display(0, len(self.all_images_info))

            # 创建队列和线程停止事件
            self.task_queue = queue.Queue()
            self.stop_event = threading.Event()
            self.processed_count = 0  # 重置计数器
            self.error_files = []  # 用于记录处理失败的文件

            # 将所有图片路径放入队列
            for img_info in self.all_images_info:
                self.task_queue.put(img_info['path'])

            # 创建并启动处理线程
            self.processing_thread = threading.Thread(target=self._process_images_thread, daemon=True)
            self.processing_thread.start()

        except ValueError as ve:
            messagebox.showerror("设置错误", f"输入值无效: {ve}")
            self.start_btn.config(state=tk.NORMAL)
            if hasattr(self, 'cancel_btn'): self.cancel_btn.config(state=tk.DISABLED)
        except Exception as e:
            messagebox.showerror("错误", f"启动处理时发生错误:\n{str(e)}")
            self.start_btn.config(state=tk.NORMAL)
            if hasattr(self, 'cancel_btn'): self.cancel_btn.config(state=tk.DISABLED)

    def _process_images_thread(self):
        """后台线程任务，处理队列中的图片"""
        while not self.task_queue.empty():
            if self.stop_event.is_set():
                print("处理被取消")
                break

            img_path = self.task_queue.get()
            success = self._process_single_image(img_path)
            if success:
                # 更新计数器（在主线程中更新UI）
                self.root.after(0, self._update_progress_ui, 1)  # Increment by 1
            else:
                self.error_files.append(os.path.basename(img_path))  # 记录失败的文件名

            self.task_queue.task_done()

        # 所有任务完成或被取消后，在主线程中调用完成函数
        self.root.after(0, self._processing_finished)

    def _process_single_image(self, img_path):
        """处理单张图片（在后台线程中运行）"""
        if self.stop_event.is_set():
            return False  # 取消处理

        try:
            img_path_obj = Path(img_path)
            if not img_path_obj.exists() or not img_path_obj.is_file():
                print(f"跳过无效文件: {img_path}")
                return True  # 认为处理"成功"（跳过），继续处理下一张

            dest_folder = self.dest_path.get()  # 获取目标文件夹

            # 尝试打开图像文件，对 RAW 文件做特殊错误处理
            try:
                img = self.open_image_file(img_path)
            except Exception as e:
                if self.is_raw_file(img_path):
                    error_msg = f"RAW文件处理失败: {str(e)}"
                    self.skip_problematic_file(img_path, error_msg)
                    return True  # 跳过此文件继续处理
                else:
                    raise e

            with img:
                # 获取原始尺寸和DPI
                original_width_raw, original_height_raw = img.size
                original_dpi = img.info.get('dpi', (72, 72))[0]
                if isinstance(original_dpi, tuple):  # DPI might be a tuple
                    original_dpi = original_dpi[0]
                original_dpi = float(original_dpi) if original_dpi else 72.0  # Fallback DPI

                # 处理EXIF方向
                try:
                    orientation = None
                    if hasattr(img, '_getexif') and img._getexif():
                        exif = img._getexif()
                        for tag, tag_value in ExifTags.TAGS.items():
                            if tag_value == 'Orientation':
                                if tag in exif:
                                    orientation = exif[tag]
                                    break
                    if orientation == 2:
                        img = img.transpose(Image.FLIP_LEFT_RIGHT)
                    elif orientation == 3:
                        img = img.transpose(Image.ROTATE_180)
                    elif orientation == 4:
                        img = img.transpose(Image.FLIP_TOP_BOTTOM)
                    elif orientation == 5:
                        img = img.transpose(Image.ROTATE_270).transpose(Image.FLIP_LEFT_RIGHT)  # Corrected order for 5
                    elif orientation == 6:
                        img = img.transpose(Image.ROTATE_270)
                    elif orientation == 7:
                        img = img.transpose(Image.ROTATE_90).transpose(Image.FLIP_LEFT_RIGHT)  # Corrected order for 7
                    elif orientation == 8:
                        img = img.transpose(Image.ROTATE_90)
                except Exception as exif_err:
                    print(f"无法处理 EXIF 方向 for {img_path}: {exif_err}")
                    pass  # 忽略EXIF错误

                # 获取旋转后的尺寸作为处理基准
                oriented_width, oriented_height = img.size

                # 确定输出DPI
                output_dpi = self.processing_user_dpi if self.processing_user_dpi is not None else original_dpi

                # --- 开始尺寸计算逻辑（已替换）---
                final_width = oriented_width
                final_height = oriented_height

                if self.processing_size_enabled:
                    # 使用旋转后的尺寸作为原始计算基准
                    calc_original_width, calc_original_height = float(oriented_width), float(
                        oriented_height)  # Use float for calculation

                    # 优先处理标准尺寸模式
                    if self.processing_standard_size_applied and self.processing_standard_size_selection != "自定义":
                        # 标准尺寸模式：根据每张图片分别计算
                        try:
                            # 获取标准尺寸的基础值
                            if hasattr(self, 'standard_sizes') and self.processing_standard_size_selection in self.standard_sizes:
                                base_width, base_height = self.standard_sizes[self.processing_standard_size_selection]
                                
                                # 根据当前图片的横竖比例确定标准尺寸方向
                                is_landscape = calc_original_width > calc_original_height
                                if is_landscape:
                                    # 横图：较大值作为宽度
                                    standard_width_inch = max(base_width, base_height)
                                    standard_height_inch = min(base_width, base_height)
                                else:
                                    # 竖图：较小值作为宽度
                                    standard_width_inch = min(base_width, base_height)
                                    standard_height_inch = max(base_width, base_height)
                                
                                # 为当前图片计算最优DPI
                                target_dpi = self.calculate_optimal_dpi_for_image(
                                    calc_original_width, calc_original_height,
                                    standard_width_inch, standard_height_inch
                                )
                                output_dpi = target_dpi
                                
                                # 将英寸转换为像素
                                final_width = int(round(standard_width_inch * target_dpi))
                                final_height = int(round(standard_height_inch * target_dpi))
                                
                                # 确保尺寸至少为1像素
                                final_width = max(1, final_width)
                                final_height = max(1, final_height)
                            else:
                                # 如果无法获取标准尺寸，使用原始尺寸
                                final_width, final_height = calc_original_width, calc_original_height
                            
                        except (ValueError, AttributeError):
                            # 如果标准尺寸解析失败，使用原始尺寸
                            final_width, final_height = calc_original_width, calc_original_height

                    # 处理批量保持比例模式 (需要检查是否为批量且尺寸不同)
                    elif self.processing_batch_maintain_ratio and len(
                            self.selected_files) > 1 and not self.processing_uniform_size:
                        # Ensure process_batch_ratio exists and handles potential errors
                        try:
                            final_width, final_height, new_dpi = self.process_batch_ratio(
                                calc_original_width, calc_original_height, original_dpi, self.processing_uniform_dpi
                            )
                            if new_dpi is not None:
                                output_dpi = new_dpi
                        except Exception as batch_err:
                            print(f"批量比例处理错误 for {img_path}: {batch_err}")
                            # Fallback or skip? Using original size for now.
                            final_width, final_height = calc_original_width, calc_original_height

                    # 处理尺寸相同但DPI不同的情况 (需要检查是否保持比例和DPI修改启用)
                    elif self.processing_is_same_size_diff_dpi and self.processing_maintain_ratio and self.processing_dpi_enabled:
                        if self.processing_current_unit == "pixel":
                            if self.processing_user_dpi is not None:
                                # 有DPI值时，使用DPI计算新尺寸
                                img_dpi = float(original_dpi)
                                if img_dpi <= 0: img_dpi = 72.0  # Fallback DPI
                                width_inch = calc_original_width / img_dpi
                                height_inch = calc_original_height / img_dpi
                                final_width = (width_inch * self.processing_user_dpi)
                                final_height = (height_inch * self.processing_user_dpi)
                            else:
                                # DPI为空时，检查用户是否输入了宽高值
                                if self.processing_modified_width and self.processing_modified_height:
                                    # 使用用户输入的宽高值 (需要转像素，但此模式下单位应为像素)
                                    try:
                                        final_width = float(self.processing_modified_width)
                                        final_height = float(self.processing_modified_height)
                                    except ValueError:
                                        final_width, final_height = calc_original_width, calc_original_height  # fallback
                                else:
                                    # 使用原始尺寸
                                    final_width, final_height = calc_original_width, calc_original_height
                        else:  # 厘米或英寸单位
                            if self.processing_user_dpi is not None:
                                # 有DPI值时，检查用户是否输入了宽高值
                                if self.processing_modified_width and self.processing_modified_height:
                                    # 使用用户输入的宽高值转换像素
                                    try:
                                        final_width = self.convert_to_pixels(
                                            self.processing_modified_width,
                                            self.processing_current_unit,
                                            self.processing_user_dpi  # 使用用户设定的DPI
                                        )
                                        final_height = self.convert_to_pixels(
                                            self.processing_modified_height,
                                            self.processing_current_unit,
                                            self.processing_user_dpi  # 使用用户设定的DPI
                                        )
                                    except ValueError:
                                        final_width, final_height = calc_original_width, calc_original_height  # fallback
                                else:
                                    # 使用原始物理尺寸，根据新DPI计算像素
                                    img_dpi = float(original_dpi)
                                    if img_dpi <= 0: img_dpi = 72.0  # Fallback DPI
                                    width_inch = calc_original_width / img_dpi
                                    height_inch = calc_original_height / img_dpi
                                    final_width = (width_inch * self.processing_user_dpi)
                                    final_height = (height_inch * self.processing_user_dpi)
                            else:
                                # DPI为空时使用原始像素尺寸
                                final_width, final_height = calc_original_width, calc_original_height
                    else:
                        # 常规情况：处理用户输入的值（vars应已包含比例计算结果）
                        target_width = calc_original_width
                        target_height = calc_original_height
                        width_modified_flag = False
                        height_modified_flag = False

                        if self.processing_modified_width:
                            try:
                                # Use output_dpi for conversion in normal mode
                                current_output_dpi = output_dpi if output_dpi > 0 else 72.0
                                target_width = self.convert_to_pixels(
                                    self.processing_modified_width,
                                    self.processing_current_unit,
                                    current_output_dpi
                                )
                                width_modified_flag = True
                            except ValueError:
                                pass  # 忽略无效输入
                        if self.processing_modified_height:
                            try:
                                # Use output_dpi for conversion in normal mode
                                current_output_dpi = output_dpi if output_dpi > 0 else 72.0
                                target_height = self.convert_to_pixels(
                                    self.processing_modified_height,
                                    self.processing_current_unit,
                                    current_output_dpi
                                )
                                height_modified_flag = True
                            except ValueError:
                                pass  # 忽略无效输入

                        # 如果保持比例，且只有一个维度被有效修改，重新计算另一个维度
                        if self.processing_maintain_ratio:
                            if width_modified_flag and not height_modified_flag:
                                if calc_original_width > 0:  # 避免除零
                                    aspect_ratio = calc_original_height / calc_original_width
                                    target_height = target_width * aspect_ratio
                            elif height_modified_flag and not width_modified_flag:
                                if calc_original_height > 0:  # 避免除零
                                    inv_aspect_ratio = calc_original_width / calc_original_height
                                    target_width = target_height * inv_aspect_ratio

                        final_width = target_width
                        final_height = target_height

                # --- 结束尺寸计算逻辑 ---

                # 确保宽度和高度是整数且至少为1
                final_width = max(1, int(round(final_width)))
                final_height = max(1, int(round(final_height)))

                # 调整尺寸（如果需要）
                resized_img = img
                # Check if resizing is needed AND allowed (resampling enabled)
                if (final_width != oriented_width or final_height != oriented_height):
                    if self.processing_resampling_enabled:
                        # --- 复制 hjz草稿1.py 的重采样逻辑 ---
                        try:
                            is_enlarging = final_width > oriented_width or final_height > oriented_height
                            # Simple heuristic for line art detection might need refinement
                            is_line_art = img.mode in ['1', 'L', 'P']  # Assume P mode might be line art

                            if is_enlarging:
                                # Use BICUBIC for potential line art/graphics, LANCZOS for photos
                                resample_method = Image.BICUBIC if is_line_art else Image.LANCZOS
                            else:
                                # Use BICUBIC for potential line art/graphics, LANCZOS for photos
                                resample_method = Image.BICUBIC if is_line_art else Image.LANCZOS

                            resized_img = img.resize((final_width, final_height), resample_method)

                            # 锐化 (如果适用) - Conditional import
                            output_ext_temp = self.get_output_extension(img_path_obj)  # Use instance method
                            if output_ext_temp.lower() == '.png' and not is_enlarging and resized_img.mode not in ['P',
                                                                                                                   '1']:
                                try:
                                    from PIL import ImageFilter
                                    resized_img = resized_img.filter(
                                        ImageFilter.UnsharpMask(radius=0.5, percent=80, threshold=3))
                                except ImportError:
                                    print("无法导入 ImageFilter，跳过锐化")
                        except Exception as resize_err:
                            print(f"调整尺寸错误 for {img_path}: {resize_err}")
                            # Fallback to original image if resize fails? Or skip? Skipping for now.
                            return False  # Indicate error for this file
                        # --- 结束重采样逻辑 ---
                    else:
                        # 不重新采样，只改变DPI元数据，像素不变
                        resized_img = img  # 保持原图像素
                        # Ensure final dimensions match original if not resampling
                        final_width = oriented_width
                        final_height = oriented_height
                else:
                    # Size hasn't changed, use original oriented image
                    resized_img = img

                # 确定输出格式和路径
                output_ext = self.get_output_extension(img_path_obj)  # Use instance method

                # 查找对应的图片信息以获取相对路径
                relative_path = None
                for img_info in self.all_images_info:
                    if img_info['path'] == str(img_path):
                        relative_path = img_info.get('relative_path')
                        break

                # 根据是否有相对路径来构建输出路径
                if relative_path:
                    # 保持原有文件夹结构
                    output_dir = Path(dest_folder) / relative_path.parent
                    output_dir.mkdir(parents=True, exist_ok=True)  # 创建必要的文件夹
                    output_path = output_dir / (img_path_obj.stem + output_ext)
                else:
                    # 直接输出到目标文件夹（兼容原有逻辑）
                    output_path = Path(dest_folder) / (img_path_obj.stem + output_ext)

                # 格式转换和保存参数
                # Ensure output_dpi is a tuple for saving
                save_dpi = (int(round(output_dpi)), int(round(output_dpi)))
                save_args = {'dpi': save_dpi}
                img_to_save = resized_img

                # Format conversion logic based on hjz草稿1.py
                if output_ext.lower() in ['.jpg', '.jpeg']:
                    # Handle transparency before converting to RGB
                    if img_to_save.mode in ['RGBA', 'LA', 'P']:
                        try:
                            if img_to_save.mode == 'P' and 'transparency' in img_to_save.info:
                                img_to_save = img_to_save.convert('RGBA')  # Convert P with transparency to RGBA first
                            if img_to_save.mode in ['RGBA', 'LA']:
                                background = Image.new('RGB', img_to_save.size, (255, 255, 255))
                                mask = img_to_save.split()[-1]  # Get alpha channel (A or L)
                                background.paste(img_to_save, (0, 0), mask)
                                img_to_save = background
                            elif img_to_save.mode == 'P':  # P without transparency
                                img_to_save = img_to_save.convert('RGB')
                        except Exception as bg_err:
                            print(f"背景处理错误 for {img_path}: {bg_err}, converting to RGB")
                            img_to_save = img_to_save.convert('RGB')  # Fallback conversion
                    elif img_to_save.mode != 'RGB':
                        img_to_save = img_to_save.convert('RGB')

                    # --- 映射 0-12 到 Pillow 的 20-100 质量 (优化低端) ---
                    # 使用从滑块获取并存储的用户输入 (0-12)
                    ps_quality = getattr(self, 'processing_user_quality_input', 10)  # 默认为10

                    # 应用新的映射公式: round((80 / 12) * ps_quality + 20)
                    # 限制结果在 20 到 100 之间 (最低改为20)
                    pillow_quality = max(20, min(100, round((80 / 12) * ps_quality + 20)))

                    save_args['quality'] = pillow_quality  # 使用映射后的质量
                    # --- 结束映射 ---
                    save_args['optimize'] = True
                    save_args['progressive'] = True

                    # --- 根据质量动态调整色度抽样 ---
                    # 如果用户选择的质量非常低 (0-3)，禁用色度抽样以保留更多颜色
                    if 0 <= ps_quality <= 3:
                        save_args['subsampling'] = 0  # 0 对应 4:4:4 (无抽样)
                    # else: # 对于中高质量 (4-12)，不设置此参数，让Pillow/libjpeg默认处理
                    #     pass # 让库根据质量决定默认抽样 (通常是 4:2:0 或 4:2:2)
                    # --- 结束色度抽样调整 ---

                elif output_ext.lower() == '.png':
                    save_args['optimize'] = True
                    save_args['compress_level'] = 9  # Max compression
                    # Preserve palette and transparency info if possible
                    if img.mode == 'P' and 'palette' in img.info:
                        # Pillow handles palette saving automatically for P mode usually
                        pass
                    if 'transparency' in img.info and output_ext.lower() == '.png':
                        # Pillow handles transparency saving automatically for PNG
                        pass
                elif output_ext.lower() in ['.heic', '.heif']:
                    # HEIC格式保存参数
                    if HEIC_SUPPORTED:
                        # 使用与JPEG相同的质量映射
                        ps_quality = getattr(self, 'processing_user_quality_input', 10)
                        pillow_quality = max(20, min(100, round((80 / 12) * ps_quality + 20)))
                        save_args['quality'] = pillow_quality
                        # HEIC格式通常需要RGB模式
                        if img_to_save.mode not in ['RGB', 'RGBA']:
                            img_to_save = img_to_save.convert('RGB')
                    else:
                        # 如果不支持HEIC，转换为JPG格式保存
                        output_path = output_path.with_suffix('.jpg')
                        ps_quality = getattr(self, 'processing_user_quality_input', 10)
                        pillow_quality = max(20, min(100, round((80 / 12) * ps_quality + 20)))
                        save_args['quality'] = pillow_quality
                        save_args['optimize'] = True
                        save_args['progressive'] = True
                        if img_to_save.mode not in ['RGB']:
                            img_to_save = img_to_save.convert('RGB')

                # 保存图片
                img_to_save.save(output_path, **save_args)

            return True  # 处理成功
        except Exception as e:
            print(f"处理图片 {img_path} 时发生严重错误: {e}")
            # 记录错误
            import traceback
            traceback.print_exc()
            return False  # 处理失败

    def _update_progress_ui(self, increment):
        """更新进度条和标签（在主线程中调用）"""
        if self.stop_event.is_set():
            return  # Stop updating if cancelled

        self.processed_count += increment
        try:
            max_val = len(self.all_images_info)  # Get max value from the source list
            if max_val > 0:
                # --- 使用新的方法更新 Canvas ---
                # percentage = int((self.processed_count / max_val) * 100)
                # self.progress['value'] = self.processed_count
                # progress_text = f"{percentage}% ({self.processed_count}/{max_val})"
                # self.progress_label.config(text=progress_text)
                self.update_progress_display(self.processed_count, max_val)
            else:
                # Handle case where there are no files to process
                # self.progress['value'] = 0
                # self.progress_label.config(text="0% (0/0)")
                self.update_progress_display(0, 0)

            self.root.update_idletasks()  # Update UI immediately
        except Exception as ui_err:
            print(f"更新进度UI时出错: {ui_err}")
            # Don't let UI errors stop processing logic

    def _processing_finished(self):
        """处理完成后的清理工作（在主线程中调用）"""
        # 重新启用开始按钮，禁用取消按钮
        self.start_btn.config(state=tk.NORMAL)
        if hasattr(self, 'cancel_btn'): self.cancel_btn.config(state=tk.DISABLED)

        # 显示完成消息
        if self.stop_event.is_set():
            messagebox.showwarning("取消", "图片处理已取消。")
        elif self.error_files:
            error_list = "\n - ".join(self.error_files)
            messagebox.showwarning("部分完成", f"图片处理完成，但以下文件处理失败:\n - {error_list}")
        else:
            messagebox.showinfo("完成", "图片处理完成！")

        # 清理状态
        self.stop_event.clear()
        self.error_files = []
        # Clean up processing variables? Optional.
        # del self.processing_quality, self.processing_uniform_dpi, ...

        # --- 确保处理结束后 Canvas 显示最终状态 ---
        final_count = self.processed_count
        max_val = len(self.all_images_info) if self.all_images_info else 0
        self.update_progress_display(final_count, max_val)

    def cancel_processing_task(self):
        """取消当前正在运行的处理任务"""
        if hasattr(self, 'stop_event'):
            self.stop_event.set()
            print("发送取消信号...")
            # Optionally disable cancel button immediately
            if hasattr(self, 'cancel_btn'): self.cancel_btn.config(state=tk.DISABLED)

    def update_progress_display(self, current_value, max_value):
        """使用 Canvas 绘制自定义进度条和文本"""
        canvas = self.progress_canvas
        # 确保 canvas 存在且窗口可见
        try:
            # 修改：在获取尺寸前强制更新
            canvas.update_idletasks()
            width = canvas.winfo_width()
            height = canvas.winfo_height()
        except tk.TclError:
            # 窗口可能已关闭
            return

        if width <= 1 or height <= 1:  # Canvas 可能尚未完全绘制
            # 延迟后重试
            canvas.after(50, lambda: self.update_progress_display(current_value, max_value))
            return

        canvas.delete("all")  # 清除旧的绘制内容

        # 计算进度比例
        if max_value > 0:
            progress_ratio = current_value / max_value
        else:
            progress_ratio = 0
        progress_pixels = width * progress_ratio

        # 1. 绘制背景 (整个 Canvas 宽度)
        canvas.create_rectangle(0, 0, width, height, fill=self.progress_bg_color, outline="")

        # 2. 绘制进度条填充
        if progress_pixels > 0:
            canvas.create_rectangle(0, 0, progress_pixels, height, fill=self.progress_bar_color, outline="")

        # 3. 准备并绘制文本
        if max_value > 0:
            percentage = int(progress_ratio * 100)
            progress_text = f"{percentage}% ({current_value}/{max_value})"
        else:
            progress_text = "0% (0/0)"  # 或其他初始/空状态文本

        # 决定文本颜色
        # 如果进度条填充超过了中点，文字用浅色，否则用深色
        text_color = self.text_color_light if progress_ratio >= 0.5 else self.text_color_dark

        # 获取字体大小
        font_size = max(9, int(9 * self.scale_factor))  # 使用与控件一致的字体逻辑
        progress_font = ('Microsoft YaHei UI', font_size)

        # 绘制文本
        canvas.create_text(
            width / 2,  # x 坐标居中
            height / 2,  # y 坐标居中
            text=progress_text,
            fill=text_color,
            font=progress_font,
            anchor="center"
        )

    def update_quality_label_chinese(self, value):
        """根据滑块值更新中文质量标签"""
        try:
            val = int(float(value))  # 滑块值可能是浮点数，转为整数
            self.quality_slider_var.set(val)  # 确保IntVar同步

            # 更新数值标签
            self.quality_value_label.config(text=str(val))

            # 更新描述标签
            if 0 <= val <= 3:
                desc = "低"
            elif 4 <= val <= 7:
                desc = "中"
            elif 8 <= val <= 10:
                desc = "高"
            else:  # 11-12
                desc = "最佳"
            self.quality_description_label.config(text=desc)
        except Exception as e:
            print(f"更新质量标签时出错: {e}")
            self.quality_value_label.config(text="?")
            self.quality_description_label.config(text="错误")